//
//  NSData+RCAES.m
//  StructToolsDemo
//
//  Created by 杨晓辉 on 2017/8/22.
//  Copyright © 2017年 杨晓辉. All rights reserved.
//

#import "NSData+RCAES.h"

#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>

//static NSString *const PSW_AES_KEY = @"TESTPASSWORD"; // 秘钥
//static NSString *const offset = @"1234567890123456"; // 偏移量(16位)
@implementation NSData (RCAES)
/**
 AES(CBC模式)加密
 @param key 秘钥
 @param offset 偏移量
 @param sourceData 待加密的数据
 @return 加密后的结果
 */
+ (NSData *)CBCEncryptAESWithKey:(NSString*)key andOffset:(NSString*)offset andSourceData:(NSData*)sourceData;
{
    NSData *AESData = [self CBCAESoperation:kCCEncrypt
                                       data:sourceData
                                        key:key
                                         iv:offset];

    return AESData;
    
}

/**
 AES(CBC模式)解密
 @param key 秘钥
 @param offset 偏移量
 @param sourceData 待解密的数据
 @return 解密后的结果
 */
+ (NSData *)CBCDecryptAESWithKey:(NSString*)key andOffset:(NSString*)offset andSourceData:(NSData*)sourceData;
{
    
    
    
    NSData *AESData = [self CBCAESoperation:kCCDecrypt
                                       data:sourceData
                                        key:key
                                         iv:offset];
    return AESData;
    
}

/**
 *   (CBC模式)加密解密算法
 *
 *  @param operation kCCEncrypt（加密）kCCDecrypt（解密）
 *  @param data      待操作Data数据
 *  @param key       key
 *  @param iv        向量
 *
 *  @return 返回密文
 */
+ (NSData *)CBCAESoperation:(CCOperation)operation data:(NSData *)data key:(NSString *)key iv:(NSString *)iv {
    char keyPtr[kCCKeySizeAES128 + 1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    // IV
    char ivPtr[kCCBlockSizeAES128 + 1];
    bzero(ivPtr, sizeof(ivPtr));
    [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    
    NSUInteger dataLength = [data length];
    // buffer缓冲，缓冲区
    //  对于块加密算法：输出的大小<= 输入的大小 +  一个块的大小
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    //  *malloc()*函数其实就在内存中找一片指定大小的空间
    void *buffer = malloc(bufferSize);
    // size_t的全称应该是size type，就是说“一种用来记录大小的数据类型”。通常我们用sizeof(XXX)操作，这个操作所得到的结果就是size_t类型。
    // 英文翻译：num 数量 Byte字节  encrypt解密
    size_t numBytesEncrypted = 0;
    // **<CommonCrypto/CommonCryptor.h>框架下的类与方法**p苹果提供的
    // CCCrypt()  第一个参数 是告诉函数执行加密、解密过程,第二个kCCAlgorithmAES128 便是告诉函数执行DES加密.
    //     第三个参数是 kCCOptionPKCS7Padding ,其实单单这个参数就是告诉了函数 运用CBC加密模式,如果我们第三个变量写成kCCOptionPKCS7Padding|kCCOptionECBMode，就表示运用了ECB加密模式，并且使用PKCS7Padding的填充模式进行加密。
//    加密数据包单位长度是8字节，在8字节的情况下PKCS7Padding 等价与 PKCS5Padding
//
    
    CCCryptorStatus cryptStatus = CCCrypt(
                                          operation,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          ivPtr,
                                          [data bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesEncrypted);
    
    if(cryptStatus == kCCSuccess) {
        NSLog(@"Success");
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
        
    } else {
        NSLog(@"Error");
    }
    
    free(buffer);
    return nil;
}

/**
 AES(ECB模式)加密
 @param key 秘钥
 @param sourceData 待加密的数据
 @return 加密后的结果
 */
+ (NSData *)ECBEncryptAESWithKey:(NSString*)key andSourceData:(NSData*)sourceData;
{
    return [self ECBAESoperation:kCCEncrypt data:sourceData key:key];
}

/**
 AES(ECB模式)解密
 @param key 秘钥
 @param sourceData 待解密的数据
 @return 解密后的结果
 */
+ (NSData *)ECBDecryptAESWithKey:(NSString*)key andSourceData:(NSData*)sourceData;
{
      return [self ECBAESoperation:kCCDecrypt data:sourceData key:key];
}

/**
 *  (ECB模式)加密解密算法
 *  @param data      待操作Data数据
 *  @param key       key
 *
 *  @return 返回密文
 */
+ (NSData *)ECBAESoperation:(CCOperation)operation data:(NSData *)data key:(NSString *)key {
    
    char keyPtr[kCCKeySizeAES128+1];
    // [sizeof](keyPtr) 数组keyPtr所占空间的大小，即多少个个字节
    // bzero的作用是字符数组keyPtr的前sizeof(keyPtr)个字节为零且包括‘\0’。就是前32位为0，最后一位是\0
    bzero(keyPtr, sizeof(keyPtr));
    // NSString转换成C风格字符串
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    
    NSUInteger dataLength = [data length];
    // buffer缓冲，缓冲区
    //  对于块加密算法：输出的大小<= 输入的大小 +  一个块的大小
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    //  *malloc()*函数其实就在内存中找一片指定大小的空间
    void *buffer = malloc(bufferSize);
    // size_t的全称应该是size type，就是说“一种用来记录大小的数据类型”。通常我们用sizeof(XXX)操作，这个操作所得到的结果就是size_t类型。
    // 英文翻译：num 数量 Byte字节  encrypt解密
    size_t numBytesEncrypted = 0;
    // **<CommonCrypto/CommonCryptor.h>框架下的类与方法**p苹果提供的
    CCCryptorStatus cryptStatus = CCCrypt(
                                          operation,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding|kCCOptionECBMode,
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          NULL,
                                          [data bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    free(buffer);
    return nil;
    
}

@end
